////////////////////////////////////////////////////////////////////////////////
//
//  MICROSOFT CORPORATION
//  Copyright (c) Microsoft Corporation.
//  All Rights Reserved.
//
//  NOTICE: Microsoft Confidential. Intended for Internal Use Only.
//
////////////////////////////////////////////////////////////////////////////////

package com.msn.beet.logging
{

import com.msn.beet.core.Application;
import com.msn.beet.core.StaticClass;

import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

/**
 * Static class for sending logging events to a variety of logging clients.
 * Supports log level throttling and preprocessor injection of additional
 * values after original values.
 *
 * @see TraceLogger
 */
public class Log extends StaticClass
{
    include "../core/Version.as";

    private static var loggers:Dictionary = new Dictionary();

    private static var _logLevel:LogLevel = LogLevel.DEBUG;

    /**
     * Used as a placeholder by preprocessor to denote additional
     * parameters.
     * @return
     */
    public static function get sentinel():Class
    {
        return Sentinel;
    }

    /**
     * Set log level throttling.
     * @param value
     */
    public static function set logLevel(value:LogLevel):void
    {
        _logLevel = value;
    }

    public static function get logLevel():LogLevel
    {
        return _logLevel;
    }

    /**
     * Adds a logHandler as a subscriber to Log notifications.
     * @param logger
     */
    public static function addLogger(logger:ILogHandler):void
    {
        var className:String = getQualifiedClassName(logger);
        var classDef:Class = getDefinitionByName(className) as Class;

        if (loggers[classDef])
        {
            throw new ArgumentError(getQualifiedClassName(logger) +
                                    " instance already added to Log.");
        }
        else
        {
            loggers[classDef] = logger;
        }
    }

    /**
     * Returns log handler instance or null if there is no instance.
     * @param loggerType
     * @return
     */
    public static function getLogger(loggerType:Class):ILogHandler
    {
        if (loggers[loggerType])
        {
            return loggers[loggerType];
        }
        else
        {
            return null;
        }
    }

    /**
     * Removes reference to logHandler instance from Log utilitiy, returns
     * log handler instance or null if there is no instance.
     *
     * @param loggerType
     * @return
     */
    public static function removeLogger(loggerType:Class):ILogHandler
    {
        if (loggers[loggerType])
        {
            var logger:ILogHandler = loggers[loggerType];
            delete loggers[loggerType];
            return logger;
        }
        else
        {
            return null;
        }
    }

    /**
     * Replacement for native trace function.
     * @param rest
     */
    public static function debug(... rest):void
    {
        notify(LogLevel.DEBUG, rest);
    }

    /**
     *
     * @param rest
     */
    public static function info(... rest):void
    {
        notify(LogLevel.INFO, rest);
    }

    /**
     * Use to output non-critical errors.
     * @param rest
     */
    public static function warn(... rest):void
    {
        notify(LogLevel.WARN, rest);
    }

    /**
     * Use for general error logging.
     * @param rest
     */
    public static function error(... rest):void
    {
        notify(LogLevel.ERROR, rest);
    }

    /**
     * Use to log critical errors.
     * @param rest
     */
    public static function fatal(... rest):void
    {
        notify(LogLevel.FATAL, rest);
    }


    /**
     * Checks if log event is of required log level, removes preprocessor
     * values from arguments array, constructs Message instance, passes
     * message instance to all subscribers.
     * @param level
     * @param rest
     */
    private static function notify(level:LogLevel,
                                   injectedParams:Array /* of Object */):void
    {
        // check that calling method passes log level test before doing
        // anything

        if (_logLevel.value <= level.value)
        {
            var className:String = "";
            var line:int = -1;
            var cppVals:Array /* of Object */ = [];

            // get preprocessor placeholder index
            var sentinelIndex:int = injectedParams.lastIndexOf(Log.sentinel);

            // remove with trailing parameters
            if (sentinelIndex != -1)
            {
                cppVals = injectedParams.splice(sentinelIndex);
                className = cppVals[1] as String;
                line = cppVals[2] as int;
                // provide additional values to message object
                cppVals = cppVals.splice(3);
            }

            var message:Message = new Message(injectedParams, level,
                                              Application.upTime, new Date(),
                                              className, line, cppVals);

            // notify subscribers
            for (var p:Object in loggers)
            {
                var logger:ILogHandler = loggers[p];
                logger.notify(message);
            }

        }
    }

}

}

/**
 * @private
 * Used as a placeholder by preprocessor to denote additional parameters.
 */
internal class Sentinel {}
